package cloud.pangui.core.mongo.template;

import cloud.pangui.core.mongo.operate.Delete;
import cloud.pangui.core.mongo.operate.Query;
import cloud.pangui.core.mongo.operate.Update;
import com.mongodb.MongoException;
import com.mongodb.client.AggregateIterable;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 查询辅助，主要用户sql查询
 * 例如queryObjects(mongoTemplate,Query.fromSql("select * from User"),User.class);
 *
 * @Auther hepangui
 * @Date 2020/10/06 0001
*/
public class MongoTemplateUtils {

	/**
	 * logger
	 */
	static Logger logger = LoggerFactory.getLogger(MongoTemplateUtils.class);

	/**
	 * 查询对象
	 *
	 * @param mongoTemplate
	 * @param query
	 * @param clazz
	 * @param <T>
	 * @return
	 */
	public static <T> List<T> queryObjects(MongoTemplate mongoTemplate, Query query, Class<T> clazz) {
		List<Document> documents = query.toQueryDocument();
		recordLog(documents);
		AggregateIterable<Document> aggregate = mongoTemplate.execute(query.getCollectionName(), (mongoCollection -> mongoCollection.aggregate(documents).allowDiskUse(true)));
		MongoCursor<Document> iterator = aggregate.iterator();
		MongoConverter converter = mongoTemplate.getConverter();
		List<T> list = new ArrayList<>();
		try {
			while (iterator.hasNext()) {
				Document next = iterator.next();
				T convert = converter.read(clazz, next);
				list.add(convert);
			}
			return list;
		} finally {
			iterator.close();
		}
	}

	/**
	 * 查询值对象
	 * 例如 select name from user
	 *
	 * @param mongoTemplate
	 * @param query
	 * @param clazz
	 * @param <T>
	 * @return
	 */
	public static <T> List<T> queryValues(MongoTemplate mongoTemplate, Query query, Class<T> clazz) {
		List<Document> documents = query.toQueryDocument();
		recordLog(documents);
		AggregateIterable<Document> aggregate = mongoTemplate.execute(query.getCollectionName(), (mongoCollection -> mongoCollection.aggregate(documents).allowDiskUse(true)));
		MongoCursor<Document> iterator = aggregate.iterator();
		MongoConverter converter = mongoTemplate.getConverter();
		List<T> list = new ArrayList<>();
		try {
			while (iterator.hasNext()) {
				Document next = iterator.next();
				Map convert = converter.read(Map.class, next);
				if (convert.keySet().size() != 1) {
					throw new MongoException("查询的列不为1！");
				}
				Object o = convert.values().stream().findFirst().get();
				if (clazz.isAssignableFrom(o.getClass())) {
					list.add((T) o);
				} else {
					throw new MongoException("查询类型有误：期望[" + clazz.getSimpleName() + "],实际[" + o.getClass().getSimpleName() + "]");
				}
			}
			return list;
		} finally {
			iterator.close();
		}
	}

	/**
	 * 打印documet日志
	 *
	 * @param documents
	 */
	private static void recordLog(List<Document> documents) {
		if (logger.isTraceEnabled()) {
			StringBuilder sb = new StringBuilder("[QUERY][");
			documents.stream().forEach((x) -> sb.append(x.toJson()).append(","));
			sb.append("]");
			logger.trace(sb.toString());
		}
	}

	/**
	 * 查询count
	 *
	 * @param mongoTemplate
	 * @param query
	 * @return
	 */
	public static int queryCount(MongoTemplate mongoTemplate, Query query) {
		List<Document> documents = query.toCountDocument();
		recordLog(documents);
		AggregateIterable<Document> aggregate = mongoTemplate.execute(query.getCollectionName(), (mongoCollection -> mongoCollection.aggregate(documents).allowDiskUse(true)));
		MongoCursor<Document> iterator = aggregate.iterator();
		MongoConverter converter = mongoTemplate.getConverter();
		try {
			while (iterator.hasNext()) {
				Document next = iterator.next();
				Map map = converter.read(Map.class, next);
				return (Integer) map.get("count");
			}
			return 0;
		} finally {
			iterator.close();
		}
	}


	/**
	 * 条件更新
	 *
	 * @param mongoTemplate
	 * @param update
	 * @return
	 */
	public static UpdateResult update(MongoTemplate mongoTemplate, Update update) {
		if(logger.isTraceEnabled()){
			logger.trace("[UPDATE][WHERE]"+update.getWhereDoc().toJson());
			logger.trace("[UPDATE][SET]"+update.getSetDoc().toJson());
		}
		return mongoTemplate.execute(update.getCollectionName(), (collection) ->
				collection.updateMany(update.getWhereDoc(), update.getSetDoc())
		);
	}

	/**
	 * 条件删除
	 *
	 * @param mongoTemplate
	 * @param delete
	 * @return
	 */
	public static DeleteResult delete(MongoTemplate mongoTemplate, Delete delete) {
		if(logger.isTraceEnabled()){
			logger.trace("[DELETE]"+delete.getWhereDoc().toJson());
		}
		return mongoTemplate.execute(delete.getCollectionName(), (collection) ->
				collection.deleteMany(delete.getWhereDoc())
		);
	}
}
